package network

import (
	"bufio"
	"bytes"
	"encoding/binary"
	"encoding/json"
	"fmt"
	"github.com/gogf/gf/errors/gerror"
	"github.com/pkg/errors"
	"gitlab.local/DO-module/new-filemanage-module/src/constant"
	"net"
	"os"
	"reflect"
	"strconv"
	"strings"

	"gitlab.local/golibrary/utils"
	"gopkg.in/ini.v1"
)

// GetLanIp 获取Lan IP
func (n *NetDevivce) GetLanIp() (string, error) {
	info, ok := n.GetInterfaces()[n.DefaultRJ45()]
	if !ok {
		return "", gerror.New("interface not found")
	}
	v4 := info.IPv4.To4()
	if v4 != nil {
		return v4.String(), nil
	}
	v6 := info.IPv6.To4()
	if v6 != nil {
		return v6.String(), nil
	}
	return "", gerror.New("not found")
}

// GetInterfaces 获取实时网卡信息与网卡配置信息合并
func (n *NetDevivce) GetInterfaces() (result map[string]*NetInfo) {
	result = make(map[string]*NetInfo)
	interfaces, err := net.Interfaces()
	if err != nil {
		return
	}
	way, _ := n.GetDefaultGateWay()
	n.listLock.Lock()
	defer n.listLock.Unlock()
	for _, iface := range interfaces {
		if !strings.HasPrefix(iface.Name, "eth") && !strings.HasPrefix(iface.Name, "bond") && !strings.HasPrefix(iface.Name, "wlan") {
			continue
		}
		var (
			ni *NetInfo
			ok bool
		)
		if ni, ok = n.netlist[iface.Name]; !ok {
			ni = &NetInfo{DHCP: true, MTU: 1500}
		}
		ni.Name = iface.Name
		if gw, ok := way[ni.Name]; ok {
			ni.Getway = gw
		}
		addrs, err := iface.Addrs()
		if err != nil {
			continue
		}
		n.parseAddress(addrs, ni)
		ni.MTU = iface.MTU
		ni.HardAddr = iface.HardwareAddr.String()

		ni.State = ni.IsEnable()
		result[ni.Name] = ni
		n.netlist[ni.Name] = ni
	}
	return
}

// DefaultRJ45 读取默认网关配置
func (n *NetDevivce) DefaultRJ45() (result string) {
	result = "eth0"
	defaultGW, err := n.configService.GetString("defaultgwdev")
	if err != nil || defaultGW == "" {
		return
	}
	content, err := n.loadProcNetDevice()
	if err != nil {
		return
	}
	if strings.Contains(content, defaultGW) {
		result = defaultGW
	}
	return
}

// GetDefaultGateWay 获取默认网关
func (n *NetDevivce) GetDefaultGateWay() (result map[string]net.IP, errput error) {
	result = make(map[string]net.IP)
	errput = nil
	if !utils.Exists(n.route) {
		errput = fmt.Errorf("[%s] isn't exists", n.route)
		return
	}
	output, _ := os.ReadFile(n.route)
	parseStruct, err := parseToLinuxRouteStruct(output)
	if err != nil {
		errput = err
		return
	}
	gatewayHex := "0x" + parseStruct.Gateway
	gwRaw, _ := strconv.ParseInt(gatewayHex, 0, 64)
	if gwRaw != 0 {
		netgw := make(net.IP, 4)
		binary.LittleEndian.PutUint32(netgw, uint32(gwRaw))
		result[parseStruct.Iface] = netgw
	}
	return
}

// parseAddress 从[]net.Addr列表中解析ip4,ip6,netmask,netbits等信息
func (n *NetDevivce) parseAddress(addrs []net.Addr, ni *NetInfo) {
	for _, addr := range addrs {
		ip := net.IP{}
		switch v := addr.(type) {
		case *net.IPNet:
			ip = v.IP
		case *net.IPAddr:
			ip = v.IP
		}
		if ip.To4() != nil {
			ni.IPv4 = ip.To4()
			_, netmask, err := net.ParseCIDR(addr.String())
			if err == nil {
				cidr, bits := netmask.Mask.Size()
				if bits == 32 {
					ni.NetMask = net.IP(netmask.Mask).String()
					ni.NetBits = cidr
				}
			}
		}
		if ip.To16() != nil {
			if ip.To4().String() != ip.To16().String() {
				if ip.IsLinkLocalUnicast() { // 是否时链路本地单播地址(局域网)
					ni.IPv6 = ip
				} else {
					ni.PublicIpv6 = ip
				}
			} else {
				ni.PublicIpv6 = nil
				ni.IPv6 = nil
			}
		}
	}
	return
}

func parseToLinuxRouteStruct(output []byte) (linuxRouteStruct, error) {
	const (
		sep              = "\t" // field separator
		destinationField = 1    // field containing hex destination address
		gatewayField     = 2    // field containing hex gateway address
	)
	scanner := bufio.NewScanner(bytes.NewReader(output))
	if !scanner.Scan() {
		return linuxRouteStruct{}, errors.New("Invalid linux route file")
	}
	for scanner.Scan() {
		row := scanner.Text()
		tokens := strings.Split(row, sep)
		if len(tokens) < 11 {
			return linuxRouteStruct{}, fmt.Errorf("invalid row '%s' in route file: doesn't have 11 fields", row)
		}
		destinationHex := "0x" + tokens[destinationField]
		destination, err := strconv.ParseInt(destinationHex, 0, 64)
		if err != nil {
			return linuxRouteStruct{}, fmt.Errorf(
				"parsing destination field hex '%s' in row '%s': %w",
				destinationHex,
				row,
				err,
			)
		}
		// The default interface is the one that's 0
		if destination != 0 {
			continue
		}

		return linuxRouteStruct{
			Iface:       tokens[0],
			Destination: tokens[1],
			Gateway:     tokens[2],
			Flags:       tokens[3],
			RefCnt:      tokens[4],
			Use:         tokens[5],
			Metric:      tokens[6],
			Mask:        tokens[7],
			MTU:         tokens[8],
			Window:      tokens[9],
			IRTT:        tokens[10],
		}, nil
	}
	return linuxRouteStruct{}, errors.New("interface with default destination not found")
}

func (n *NetDevivce) loadProcNetDevice() (string, error) {
	b, err := os.ReadFile("/proc/net/dev")
	if err != nil {
		return "", errors.Wrap(err, "read /proc/net/dev failed")
	}
	return string(b), nil
}

// GetIntranetIpv4 获取内网ipv4
func (n *NetDevivce) GetIntranetIpv4() string {
	load, e := ini.Load(constant.SysDefault)
	if e != nil {
		return ""
	}
	defaultgwdev := load.Section("system").Key("defaultgwdev").Value()
	if defaultgwdev == "" {
		return ""
	}
	ipv4, _ := utils.ShellExec(fmt.Sprintf(`ifconfig %s | awk '/inet / {print $2}'`, defaultgwdev))
	return strings.TrimSpace(ipv4)
}

func (ni *NetInfo) IsEnable() (result bool) {
	carrier := utils.GetFirstLine(fmt.Sprintf("/sys/class/net/%s/carrier", ni.Name))
	if carrier != "" {
		result, _ = strconv.ParseBool(carrier)
	}
	return
}

func (ni *NetInfo) String() string {
	marshal, _ := json.Marshal(ni)
	return string(marshal)
}

func (ni NetInfo) IsEmpty() bool {
	return reflect.DeepEqual(ni, NetInfo{})
}
